Kapitel 3 Fehler
In diesem Kapitel geht es um Fehler. Dabei gibt es zweierlei Themenbereiche: logische Fehler, die der Programmierer macht, und Fehler, die im Laufe des Programms auftreten, sei es durch falsche Benutzereingaben, sei es durch Fehler, die das System produziert. Erstere müssen gesucht werden, letztere können abgefangen werden.
3.1 Fehler abfangen
 
Dynamische Dialoge sind eine gute Hilfe, um den Benutzer zu führen: Er kann nur aus einer Liste auswählen, nur eine Zahl in ein Textfeld eingeben oder es sind gewisse Kombinationen gesperrt. Möglicherweise schafft er es aber dennoch das System zu überlisten oder die fehlerhafte Variante, die er eingibt, wurde von Seiten des Programmierers nicht bedacht.
Nehmen wir das Primzahlenbeispiel des letzten Kapitels: Der Benutzer gibt eine Zahl ein, klickt auf einen Button und es wird gemeldet, ob diese Zahl eine Primzahl ist oder nicht. Wenn er allerdings eine negative Zahl eingibt, kann keine Wurzel gezogen werden, was zu einer Fehlermeldung führt. Gibt er Text ein, erhält er ebenfalls eine Fehlermeldung.
Die Funktion IsNumeric wurde schon in den ersten beiden Kapiteln erwähnt, hier nun die vollständige Liste aller Funktionen, mit denen bestimmte Kriterien überprüft werden können:
Tabelle 3.1
Die Validierungsfunktionen
| Funktion
|
Bedeutung
|
| IsArray
|
überprüft, ob die Variable ein Array ist
|
| IsDate
|
überprüft, ob das eingegebene Datum gültig ist
|
| IsDBNull
|
überprüft, ob ein Wert DBNull ist. Dies wird für Datenbanken verwendet
|
| IsError
|
überprüft, ob ein Ausdruck ein Exception-Objekt ergibt
|
| IsNothing
|
überprüft, ob ein Ausdruck Nothing ist
|
| IsNumeric
|
überprüft, ob ein Ausdruck eine Zahl ist
|
| IsReference
|
überprüft, ob ein Ausdruck einen Referenztypen ergibt
|
Man könnte nun verschiedene Varianten abfangen. Das folgende Beispiel überprüft, ob nichts eingegeben wurde, ob Text, eine negative Zahl oder eine Dezimalzahl eingegeben wurde. Falls alle Tests negativ verlaufen, beginnt das eigentliche Programm.
If Me.txtZahl.Text = "" Then
MessageBox.Show("Bitte geben Sie" & _
" eine Zahl ein.")
ElseIf IsNumeric(Me.txtZahl.Text) = False Then
MessageBox.Show("Der eingegebene Wert" & _
" ist keine Zahl.")
ElseIf CDbl(Me.txtZahl.Text) < 0 Then
MessageBox.Show("Der eingegebene Wert" & _
" darf nicht negativ sein")
ElseIf CDbl(Me.txtZahl.Text) <> _
Math.Ceiling(CDbl(Me.txtZahl.Text)) Then
MessageBox.Show("Der eingegebene Wert" & _
" muss ganzzahlig sein")
Else
dblZahl = CDbl(Me.txtZahl.Text)
For dblZähler = 2 To Sqrt(dblZahl)
If dblZahl Mod dblZähler = 0 Then
...
Abbildung 3.1
Fehler werden abgefangen
Die Funktion Math.Ceiling ermittelt die nächst größere ganze Zahl, die größer oder gleich der eingegebenen Zahl ist. Also Math.Ceiling(2.8) ergibt 3, Math.Ceiling(8.1) ergibt 9 und Math.Ceiling(5) liefert 5.
Dieses Beispiel für das Abfangen der Eingabewerte mag bei einem kleinen Programm noch funktionieren, aber bei sehr großen, komplexen Programmen können bestimmte Fehlerquellen übersehen werden. Um auf alle Fehler, gleich welcher Natur, zu reagieren, stehen die Try ... Catch-Blöcke zur Verfügung. Die Syntax lautet:
Try
Anweisungen
Catch ex As Exception
Routinen zur Ausnahmebehandlung
Finally
Endanweisungen
End Try
Unser Primzahlenbeispiel könnte also wie folgt modifiziert werden:
Try
dblZahl = CDbl(Me.txtZahl.Text)
dblZähler = 2
Do Until dblZähler > Math.Sqrt(dblZahl)
If dblZahl Mod dblZähler = 0 Then
MessageBox.Show(dblZahl.ToString & _
" ist keine Primzahl" & vbCr & _
dblZähler.ToString & "ist ein Teiler")
Exit Sub
End If
dblZähler += 1
Loop
MessageBox.Show(dblZahl.ToString & _
" ist eine Primzahl")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
Tritt ein Fehler nach der Zeile Try auf, dann wird die Zeile
Catch ex As Exception
angesprungen. Man könnte dort eine Meldung ausgeben »Es trat ein Fehler auf«, was allerdings nicht sehr geschickt ist.
Abbildung 3.2
Der Fehler wird abgefangen - allerdings ist die Anzeige nicht sehr aussagekräftig.
Das selbst definierte Objekt »ex« besitzt nun einige Methoden und Eigenschaften, mit denen differenziert auf den Fehler eingegangen werden kann. Die einfachste Möglichkeit, sich die Beschreibung des Fehlers anzeigen zu lassen, lautet »Message«. »Source« liefert das Objekt oder die Anwendung, welche den Fehler produziert hat.
Abbildung 3.3
Message zeigt den Fehler an.
»StackTrace« liefert Informationen über die Aufrufliste, die zu dem Fehler geführt hat. Praktisch hierbei ist, dass in der letzten Zeile die Nummer erscheint, welche den Fehler auslöst. Die Zeilennummern können über Extras . Optionen . Text-Editor . Basic eingeschaltet werden.
Abbildung 3.4
StackTrace beschreibt den Fehler genau.
Abbildung 3.5
Die Zeilennummer ist für den Programmierer hilfreich. In Zeile 126 ist der Fehler aufgetreten.
Will man nun differenziert auf den Fehler reagieren, dann steht eine Reihe von Ausnahmeklassen zur Verfügung:
Tabelle 3.2
Die Liste der wichtigsten Ausnahmeklassen
| Klasse
|
Bedeutung
|
| AccessException
|
Fehler beim Zugriff auf eine Eigenschaft oder Methode
|
| ArgumentException
|
ein Argument ist ungültig
|
| ArgumentNullException
|
eine Methode erhält Nothing als Argument - sie akzeptiert es nicht
|
| ArgumentOutOfRangeException
|
das übergebene Argument liegt außerhalb des Gültigkeitsbereichs
|
| ArithmeticException
|
Unter- oder Überlauf
|
| ArrayTypeMismatchException
|
in einem Datenfeld wurde ein falscher Wert gespeichert
|
| DivedByZeroException
|
Division durch 0
|
| FormatException
|
Typen unverträglich
|
| IndexOutOfRangeException
|
Index außerhalb des gültigen Bereichs (beispielsweise bei Arrays)
|
| InvalidCastException
|
beim Umwandeln von einem Datentyp in einen anderen ist ein Fehler aufgetreten
|
| NullReferenceException
|
ein Objekt referenziert auf Nothing
|
| OutOfMemoryException
|
kein Arbeitsspeicher mehr zur Verfügung
|
| StackOverflowException
|
Überlauf
|
Soll nun nach einem abgefangenen Fehler eine Zeile abgearbeitet werden, geschieht dies mit Finally. Diese Technik ist beim Datenbankzugriff wichtig: Im Try-Block wird eine Verbindung zu einer Datenbank hergestellt. Angenommen, es kommt beim Datenaustausch zu einem Fehler - dann greift die Ausnahmebehandlungsroutine. In jedem der beiden Fälle (alles hat geklappt oder es trat ein Fehler auf) soll die Datenbankverbindung wieder geschlossen werden. Dies wird im Finally-Block erledigt. In unserem Beispiel macht es wenig Sinn, könnte aber wie folgt aussehen:
Try
dblZahl = CDbl(Me.txtZahl.Text)
dblZähler = 2
Do Until dblZähler > Math.Sqrt(dblZahl)
If dblZahl Mod dblZähler = 0 Then
MessageBox.Show(dblZahl.ToString & _
" ist keine Primzahl" & vbCr & _
dblZähler.ToString & "ist ein Teiler")
Exit Sub
End If
dblZähler += 1
Loop
MessageBox.Show(dblZahl.ToString & _
" ist eine Primzahl")
Catch ex As FormatException
MessageBox.Show("Eingabe falsch!")
Catch ex As DivideByZeroException
MessageBox.Show("Nicht durch Null teilen")
Catch ex As Exception
MessageBox.Show(ex.Message)
MessageBox.Show(ex.StackTrace)
Finally
dblZähler = 0
dblZahl = 0
End Try
|